/**
 * 
 */
package playGround.dungeon;


import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Value;

/**
 * A class representing a position in a coordinate system.
 * @author Niels Claeys, Maarten Christiaen
 * @version 2.2 
 */
@Value
public class Position {
	/**
	 * Variable registering the x coordinate of this position.
	 */
	private long xValue;
	/**
	 * Variable registering the y coordinate of this position.
	 */
	private long yValue;
	/**
	 * Variable registering the z coordinate of this position.
	 */
	private long zValue;
	/**
	 * initializes the position
	 * @param x
	 * @param y
	 * @param z
	 * @post ...
	 * 		| new.getXValue() = x
	 * 		| new.getYValue() = y
	 * 		| new.getZValue() = z
	 */
	public Position(long x, long y, long z){
		this.xValue = x;
		this.yValue = y;
		this.zValue = z;
	}
	/**
	 * initializes this position with the coordinates of the given position
	 * @param position the Position containing the coordinates you want this Position to have.
	 * @post ...
	 * 		| new.getXValue = position.getXValue()
	 * 		| new.getYValue = position.getYValue()
	 * 		| new.getZValue = position.getZValue()
	 */
	public Position(Position position){
		this(position.getXValue(),position.getYValue(),position.getZValue());
	}
	/**
	 * this method returns in which direction the position is located
	 * @param position you want to compare with this position
	 * @return
	 * 		| if(this.getYValue() == position.getYValue() && this.getZValue() == position.getZValue() && this.getXValue() == position.getXValue()+1)
	 * 		| then result == Direction.WEST
	 * 		| else if(this.getYValue() == position.getYValue() && this.getZValue() == position.getZValue() && this.getXValue() == position.getXValue()-1)
	 * 		| then result == Direction.EAST
	 * 		| else if(this.getXValue() == position.getXValue() && this.getZValue() == position.getZValue() && this.getYValue() == position.getYValue()+1)
	 * 		| then result == Direction.NORTH
	 * 		| else if(this.getXValue() == position.getXValue() && this.getZValue() == position.getZValue() && this.getYValue() == position.getYValue()-1)
	 * 		| then result == Direction.SOUTH
	 * 		| else if(this.getYValue() == position.getYValue() && this.getXValue() == position.getXValue() && this.getZValue() == position.getZValue()+1)
	 * 		| then result == Direction.FLOOR
	 * 		| else if(this.getYValue() == position.getYValue() && this.getXValue() == position.getXValue() && this.getZValue() == position.getZValue()-1)
	 * 		| then result == Direction.CEILING
	 * 		| else
	 * 		| result == null
	 */
	public Direction getDirectionOf(Position position){
		if(this.getYValue() == position.getYValue() && this.getZValue() == position.getZValue()){
			if(this.getXValue() == position.getXValue()+1)
				return Direction.WEST;
			if(this.getXValue() == position.getXValue()-1)
				return Direction.EAST;
		}
		if(this.getXValue() == position.getXValue() && this.getZValue() == position.getZValue()){
			if(this.getYValue() == position.getYValue()+1)
				return Direction.NORTH;
			if(this.getYValue() == position.getYValue()-1)
				return Direction.SOUTH;
		}
		if(this.getYValue() == position.getYValue() && this.getXValue() == position.getXValue()){
			if(this.getZValue() == position.getZValue()+1)
				return Direction.FLOOR;
			if(this.getZValue() == position.getZValue()-1)
				return Direction.CEILING;
		}
		return null;
	}
	/**
	 * This method gives the Position next to this Position and in the given direction.
	 * @param direction the direction you want the position of
	 * @return	...
	 * 			|if(direction==Direction.CEILING)
	 * 			|then result==this.getXValue(),this.getYValue(),this.getZValue()+1)
	 * 			|else if(direction==Direction.FLOOR)
	 * 			|then result==this.getXValue(),this.getYValue(),this.getZValue()-1)
	 * 			|else if(direction==Direction.NORTH)
	 * 			|then result==this.getXValue(),this.getYValue()-1,this.getZValue())
	 * 			|else if(direction==Direction.SOUTH)
	 * 			|then result==this.getXValue(),this.getYValue()+1,this.getZValue())	 
	 * 			|else if(direction==Direction.EAST)
	 * 			|then result==this.getXValue()+1,this.getYValue(),this.getZValue())
	 * 			|else if(direction==Direction.WEST)
	 * 			|then result==this.getXValue()-1,this.getYValue(),this.getZValue())
	 * 	 		|else result==null
	 */
	public Position getNeighboringPosition(Direction direction){
		if(direction==Direction.CEILING)
			return new Position(this.getXValue(),this.getYValue(),this.getZValue()+1);
		if(direction==Direction.FLOOR)
			return new Position(this.getXValue(),this.getYValue(),this.getZValue()-1);
		if(direction==Direction.NORTH)
			return new Position(this.getXValue(),this.getYValue()-1,this.getZValue());
		if(direction==Direction.EAST)
			return new Position(this.getXValue()+1,this.getYValue(),this.getZValue());
		if(direction==Direction.SOUTH)
			return new Position(this.getXValue(),this.getYValue()+1,this.getZValue());
		if(direction==Direction.WEST)
			return new Position(this.getXValue()-1,this.getYValue(),this.getZValue());
		else
			return null;
	}
	/**
	 * this method returns the x value
	 * @return: x value
	 */
	@Basic
	public long getXValue(){
		return this.xValue;
	}
	/**
	 * this method returns the y value
	 * @return y value
	 */
	@Basic
	public long getYValue(){
		return this.yValue;
	}
	/**
	 * this method returns the z value
	 * @return z value
	 */
	@Basic
	public long getZValue(){
		return this.zValue;
	}
	

	/**
	 * This method overrides the toString method from object
	 * @return the x,y,z value of the object
	 * 			|result == "("+this.getXValue()+","+this.getYValue()+","+this.getZValue()+")"
	 */
	@Override
	public String toString() {
		return "("+this.getXValue()+","+this.getYValue()+","+this.getZValue()+")";
	}
	/**
	 * this method overrides the hashcode in object.
	 * @return the new hashcode of a position object
	 * 			| int code=1
	 * 			| code = 31 * code + (int) (getXValue() ^ (getXValue() >>> 32));
	 *			| code = 31 * code + (int) (getYValue ^ (getYValue >>> 32));
	 *			| result == 31 * code + (int) (getZValue ^ (getZValue >>> 32));
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (xValue ^ (xValue >>> 32));
		result = prime * result + (int) (yValue ^ (yValue >>> 32));
		result = prime * result + (int) (zValue ^ (zValue >>> 32));
		return result;
	}
	/**
	 * This method overrides the equals method from object.
	 * @param obj the object that is compared to this Position.
	 * @return true if and only if the x,y,z value of the other object is the same
	 *    		| if(this==obj)
     *      	| then result == true
	 * 			| if(obj==null)
     *      	| then result == false
     *      	| else if (!(obj instanceof Position))
     *      	| then result==false
     *      	| Position other= (Position) obj
     *      	| else if(other.getXValue()==this.getXValue()&&other.getYValue()==this.getYValue()&&other.getZValue()==this.getZValue()
     *      	| then result == true
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Position))
			return false;
		Position other = (Position) obj;
		if (xValue != other.xValue)
			return false;
		if (yValue != other.yValue)
			return false;
		if (zValue != other.zValue)
			return false;
		return true;
	}

}
